package com.easy.mongodb.core.biz;

/**
 * @ProductName: easy-mongodb
 * @Package: com.easy.mongodb.core.biz
 * @Description: 1.mongodb包含三种单位：米(meters)、平面单位(flat units，指经纬度的“一度”)、弧度(radians，指扇形角的度数)
 * <p>
 * 2.不同的存储方式或者查询方式使用的单位不一样
 * <p>
 * ①如果数据在db中是以GeoJSON存储，则所有查询的单位都是米
 * <p>
 * ②如果查询条件是以$near查询，则查询单位是平面单位“度”。假定你的系统传进来的参数是“米”，由于$near接受的是平面单位“度”，那么在拼接语句(可以认为是sql语句)的时候需要将“米”转换成“度”:米/1000/111,意思是将“米”转换成“公里”，再转换成“度”。如果不进行转换就进行查询，例如你传入100米，mongo会认为是100度。差距有多大，可想而知。其他情况，查看下表，进行类似转换即可。
 * <p>
 * 3.米、平面单位“度”、弧度转换
 * <p>
 * ①1米=(1/1000/111)度，其中1000表示转换成公里；111表示公里转换成度，这是近似值。
 * <p>
 * ②1米=(1/1000/6371)弧度, 其中1000表示转换成公里；6371表示公里转换成弧度，以赤道为准，也是近似值。
 * @Author: vapeshop
 * @Date: 2022/6/21 11:32
 * @UpdateUser: vapeshop
 * @UpdateDate: 2022/6/21 11:32
 * @UpdateRemark: The modified content
 * @Version: 1.0
 * <p>
 * Copyright © 2022 vapeshop Technologies Inc. All Rights Reserved
 **/

public enum DistanceUnit {
    INCH(0.0254, new String[]{"in", "inch"}),
    YARD(0.9144, new String[]{"yd", "yards"}),
    FEET(0.3048, new String[]{"ft", "feet"}),
    KILOMETERS(1000.0, new String[]{"km", "kilometers"}),
    NAUTICALMILES(1852.0, new String[]{"NM", "nmi", "nauticalmiles"}),
    MILLIMETERS(0.001, new String[]{"mm", "millimeters"}),
    CENTIMETERS(0.01, new String[]{"cm", "centimeters"}),
    MILES(1609.344, new String[]{"mi", "miles"}),
    METERS(1.0, new String[]{"m", "meters"});

    public static final DistanceUnit DEFAULT = METERS;
    private final String[] names;
    private double meters;

    private DistanceUnit(double meters, String... names) {
        this.meters = meters;
        this.names = names;
    }

    public static double convert(double distance, DistanceUnit from, DistanceUnit to) {
        return from == to ? distance : distance * from.meters / to.meters;
    }

    public static double parse(String distance, DistanceUnit defaultUnit, DistanceUnit to) {
        Distance dist = DistanceUnit.Distance.parseDistance(distance, defaultUnit);
        return convert(dist.value, dist.unit, to);
    }

    public static DistanceUnit fromString(String unit) {
        DistanceUnit[] var1 = values();
        int var2 = var1.length;

        for (int var3 = 0; var3 < var2; ++var3) {
            DistanceUnit dunit = var1[var3];
            String[] var5 = dunit.names;
            int var6 = var5.length;

            for (int var7 = 0; var7 < var6; ++var7) {
                String name = var5[var7];
                if (name.equals(unit)) {
                    return dunit;
                }
            }
        }

        throw new IllegalArgumentException("No distance unit match [" + unit + "]");
    }

    public static DistanceUnit parseUnit(String distance, DistanceUnit defaultUnit) {
        DistanceUnit[] var2 = values();
        int var3 = var2.length;

        for (int var4 = 0; var4 < var3; ++var4) {
            DistanceUnit unit = var2[var4];
            String[] var6 = unit.names;
            int var7 = var6.length;

            for (int var8 = 0; var8 < var7; ++var8) {
                String name = var6[var8];
                if (distance.endsWith(name)) {
                    return unit;
                }
            }
        }

        return defaultUnit;
    }

    public double getEarthCircumference() {
        return 4.007501668557849E7 / this.meters;
    }

    public double getEarthRadius() {
        return 6378137.0 / this.meters;
    }

    public double getDistancePerDegree() {
        return 4.007501668557849E7 / (360.0 * this.meters);
    }

    public double toMeters(double distance) {
        return convert(distance, this, METERS);
    }

    public double fromMeters(double distance) {
        return convert(distance, METERS, this);
    }

    public double convert(double distance, DistanceUnit unit) {
        return convert(distance, unit, this);
    }

    public String toString(double distance) {
        return distance + this.toString();
    }

    @Override
    public String toString() {
        return this.names[0];
    }

    public double parse(String distance, DistanceUnit defaultUnit) {
        return parse(distance, defaultUnit, this);
    }

//    public static DistanceUnit readFromStream(StreamInput in) throws IOException {
//        byte b = in.readByte();
//        if (b >= 0 && b < values().length) {
//            return values()[b];
//        } else {
//            throw new IllegalArgumentException("No type for distance unit matching [" + b + "]");
//        }
//    }

    //    public void writeTo(StreamOutput out) throws IOException {
//        out.writeByte((byte)this.ordinal());
//    }
    public static class Distance implements Comparable<Distance> {
        public final double value;
        public final DistanceUnit unit;

        public Distance(double value, DistanceUnit unit) {
            this.value = value;
            this.unit = unit;
        }

        public static Distance parseDistance(String distance) {
            return parseDistance(distance, DistanceUnit.DEFAULT);
        }

        private static Distance parseDistance(String distance, DistanceUnit defaultUnit) {
            DistanceUnit[] var2 = DistanceUnit.values();
            int var3 = var2.length;

            for (int var4 = 0; var4 < var3; ++var4) {
                DistanceUnit unit = var2[var4];
                String[] var6 = unit.names;
                int var7 = var6.length;

                for (int var8 = 0; var8 < var7; ++var8) {
                    String name = var6[var8];
                    if (distance.endsWith(name)) {
                        return new Distance(Double.parseDouble(distance.substring(0, distance.length() - name.length())), unit);
                    }
                }
            }

            return new Distance(Double.parseDouble(distance), defaultUnit);
        }

        public Distance convert(DistanceUnit unit) {
            return this.unit == unit ? this : new Distance(DistanceUnit.convert(this.value, this.unit, unit), unit);
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            } else if (obj instanceof Distance) {
                Distance other = (Distance) obj;
                return DistanceUnit.convert(this.value, this.unit, other.unit) == other.value;
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return Double.valueOf(this.value * this.unit.meters).hashCode();
        }

        @Override
        public int compareTo(Distance o) {
            return Double.compare(this.value, DistanceUnit.convert(o.value, o.unit, this.unit));
        }

        @Override
        public String toString() {
            return this.unit.toString(this.value);
        }
    }
}
